#include "TypeVisitor.h"

using namespace moses;

TypeVisitor::TypeVisitor(std::ostream &err)
: m_err(err), m_errors(0)
{
}

void TypeVisitor::Accept(BinaryExpr *pItem)
{
	pItem->GetLeft()->Visit(this);
	pItem->GetRight()->Visit(this);
	
	Type rightType = PopType();
	Type leftType = PopType();
	
	if (leftType != rightType)
		Report(pItem) << "Types in binary expression does not match.\n";
		
	PushType(leftType); 
}

void TypeVisitor::Accept(Call *pItem)
{
	pItem->GetIdentifier()->Visit(this);
	
	std::string funcId = pItem->GetIdentifier()->GetString();
	Function *pFunction = GetModule()->GetFunction(funcId);
	
	if (!pFunction)
	{
		Report(pItem) 	<< "Can not resolve function '" <<
							funcId << "'.\n";
							
		PushType(Type::ANY);
		return;
	}		
					
	if (pFunction->GetParameters()->size() - 1 !=
		pItem->GetArguments()->size())
	{
		Report(pItem) <<	"Wrong number of arguments in call to "
							"function '" << funcId << "'.\n";
	} else {
		for (size_t i = 0; i < pItem->GetArguments()->size(); ++i)
		{
			Type *pParam = (*pFunction->GetParameters())[i+1];
			Expr *pArg = (*pItem->GetArguments())[i];
			
			pParam->Visit(this);
			pArg->Visit(this);
			
			Type argType = PopType();
			Type paramType = PopType();
			
			if (argType != paramType)
				Report(pItem) <<	"Type of argument " << (i+1) <<
									" does not match the signature of "
									"the function.\n"; 
		}							
	}
	
	(*pFunction->GetParameters())[0]->Visit(this);			
}

void TypeVisitor::Accept(Function *pItem)
{
	pItem->GetIdentifier()->Visit(this);
	
	for (size_t i = 0; i < pItem->GetRows()->size(); ++i)
	{							
		(*pItem->GetRows())[i]->Visit(this);
	}			
}

void TypeVisitor::Accept(FunctionRow *pItem)
{
	std::string funcId = pItem->GetIdentifier()->GetString();
	Function *pFunction = GetModule()->GetFunction(funcId);
	
	if (!pFunction)
	{
		Report(pItem) <<	"Can not find signature for function '" <<
							funcId << "'.\n";
		return;
	}
	
	if (pFunction->GetParameters()->size() - 1 !=
		pItem->GetPatternList()->size())
	{
		Report(pItem) <<	"Pattern size does not match signature "
							"of function.\n";
		return;
	}
	
	for (size_t i = 0; i < pItem->GetPatternList()->size(); ++i)
	{
		Pattern *pPattern = (*pItem->GetPatternList())[i];
		Type *pParam = (*pFunction->GetParameters())[i+1];
		
		pParam->Visit(this);
		pPattern->Visit(this);				
		
		Type patternType = PopType();
		Type paramType = PopType();
		
		if (patternType != paramType)
		{
			Report(pItem) <<	"The type of the pattern does not "
								"match the signare of the function.\n";
			break;
		} 
		
	}

	pItem->GetExpr()->Visit(this);
	(*pFunction->GetParameters())[0]->Visit(this);
	
	Type returnType = PopType();
	Type exprType = PopType();
	
	if (returnType != exprType)
		Report(pItem) << 	"The type of the expression does not "
							"match the return type of the function.\n"; 
}

void TypeVisitor::Accept(Identifier *pItem)
{
}
		
void TypeVisitor::Accept(Literal *pItem)
{
	pItem->GetValue().GetType().Visit(this);
}

void TypeVisitor::Accept(Module *pItem)
{
	m_pModule = pItem;
	pItem->GetFunctions()->VisitAll(this);
	assert(StackEmpty());
}
		
void TypeVisitor::Accept(Pattern *pItem)
{
	pItem->GetType().Visit(this);
}
		
void TypeVisitor::Accept(Type *pItem)
{
	PushType(*pItem);
}

void TypeVisitor::Accept(UnaryExpr *pItem)
{
	pItem->GetExpr()->Visit(this);
}

unsigned int TypeVisitor::NumErrors() { return m_errors; }


void TypeVisitor::PushType(Type t)
{
	m_stack.push(t);
}

Type TypeVisitor::PopType()
{
	assert(!m_stack.empty());

	Type t = m_stack.top();
	m_stack.pop();
	return t;
}

bool TypeVisitor::StackEmpty()
{
	return m_stack.empty();
}

std::ostream& TypeVisitor::Report(Item *pItem)
{
	m_errors++;
	return (m_err << "Error line " << pItem->LineNo() << ": ");
}

Module* TypeVisitor::GetModule()
{
	return m_pModule;
}
